package main

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"path"
	"path/filepath"
)

func fileExists(filePath string) bool {
	_, err := os.Stat(filePath)
	// 如果是文件不存在，执行os.IsNotExist(err)返回true。
	// 也有可能是其他错误导致os.Stat返回了err，不过我们将这些错误视为文件不存在。
	return err == nil
}

func fileDemo1() {
	fmt.Println("file io demos 1")

	//exists
	filePath := "D:\\tmp\\1234.txt"
	os.Remove(filePath)

	bExists := fileExists(filePath)
	fmt.Printf("file %s exist: %v\n", filePath, bExists)

	//create & write string to a file.
	if !bExists {
		f, err := os.Create(filePath)
		if err != nil {
			fmt.Printf("FAILED to create file %s, error: %s\n", filePath, err.Error())
		} else {
			// 使用WriteString直接写入字符串。
			f.WriteString("11111")
			f.Close()

			// 再使用追加模式往里边添加内容
			f, err = os.OpenFile(filePath, os.O_WRONLY|os.O_APPEND, 0666)
			if err != nil {
				fmt.Printf("FAILED to open file %s with append mode, error: %s\n", filePath, err.Error())
			} else {
				f.WriteString("22222")
				f.Close()
			}
		}
	}
	bExists = fileExists(filePath)
	if bExists {
		// read 2进制数据，再转字符串。
		// 使用os.File.Read
		f, err := os.Open(filePath)
		if err != nil {
			fmt.Printf("FAILED to open file %s for reading, error: %s\n", filePath, err.Error())
		} else {
			// 定义一个定长的缓冲区，长度不可以为0！否则虽然f.Read执行成功但是不会写入任何数据！
			// f.Read会返回读取的长度，需要从缓冲区取出对应的长度的数据。
			// 实际使用时这里需要一个循环，并判断err==io.EOF。
			var content []byte = make([]byte, 64)
			cnt, err := f.Read(content)
			if err != nil {
				fmt.Printf("FAILED to read file %s, error: %s\n", filePath, err.Error())
			} else {
				fmt.Printf("Read bytes %d, content: %v\n", cnt, content[0:cnt])
				// 尝试转为字符串
				s := string(content[0:cnt])
				fmt.Printf("%s\n", s)
			}
			f.Close()
		}

		// 使用ioutil.ReadFile一次性读取出文件所有内容。
		// 小文件用这种方式很方便。
		content, err := ioutil.ReadFile(filePath)
		if err != nil {
			fmt.Printf("FAILED to read file: %s, error %s\n", filePath, err.Error())
		} else {
			fmt.Printf("Read content: %v\n", content)
			// 尝试转为字符串
			s := string(content)
			fmt.Printf("%s\n", s)
		}
	}

	// 获取文件拓展名，主文件名
	fmt.Printf("extension name： %s\n", path.Ext("D:\\tmp\\1234.txt")) // .txt
	// Base name对Windows路径不友好,必须使用filepath.ToSlash转换一次。
	fmt.Printf("Base name: %s\n", path.Base("D:\\tmp\\1234.txt"))                             // "D:\\tmp\\1234.txt"
	fmt.Printf("Base name(to slash): %s\n", path.Base(filepath.ToSlash("D:\\tmp\\1234.txt"))) // "1234"
	fmt.Printf("Base name: %s\n", path.Base("/tmp/1234.txt"))                                 // "1234"
	// 获取路径，同样对Windows路径不友好,必须使用filepath.ToSlash转换一次。
	fmt.Printf("%s\n", path.Dir("D:\\tmp\\1234.txt"))                   // "."
	fmt.Printf("%s\n", path.Dir(filepath.ToSlash("D:\\tmp\\1234.txt"))) // "D:/tmp"
	fmt.Printf("%s\n", path.Dir("/tmp/1234.txt"))                       // "/tmp"

	// 复制，移动和删除文件
	{
		//使用io.Copy复制文件
		copyFrom := "D:\\tmp\\1234.txt"
		copyTo := "D:\\tmp\\4321.txt"
		src, err := os.Open(copyFrom)
		if err == nil {
			os.Remove(copyTo)
			dst, errd := os.Create(copyTo)
			if errd == nil {
				cnt, errcp := io.Copy(dst, src)
				if errcp == nil {
					fmt.Printf("Copy from %s to %s, copied %d bytes\n", copyFrom, copyTo, cnt)
				}
				dst.Close() //完成后注意关闭文件
			}
			src.Close() //完成后注意关闭文件
		}

		// 重命名文件
		renameTo := "D:\\tmp\\4444.txt"
		fmt.Printf("rename file %s to %s\n", copyTo, renameTo)
		err = os.Rename(copyTo, renameTo)
		if err != nil {
			fmt.Printf("FAILED to rename file, error: %s\n", err.Error())
		}

		// 删除文件
		os.Remove(renameTo)
	}

	// 判断是否为目录
	{
		fmt.Println("判断是否为目录")
		PathFileIsDir("D:\\tmp\\1234.txt") // false
		PathFileIsDir("D:\\tmp\\4444.txt") // Not exist.
		PathFileIsDir("D:\\tmp")           // true
	}

	// 获取目录下的文件列表
	{
		s := "D:\\tmp"
		fmt.Printf("获取目录%s下文件列表\n", s)
		dirEntries, err := os.ReadDir(s)
		if err == nil {
			for _, v := range dirEntries {
				if v.IsDir() {
					fmt.Println("Dir:", v.Name())
				} else {
					fmt.Println("File:", v.Name())
				}
			}
		}
	}

	// 递归遍历目录下的所有文件和文件夹。
	{
		s := "D:\\tmp"
		fmt.Printf("递归遍历目录%s下的所有文件和文件夹。\n", s)
		filepath.Walk(s, func(path string, fi os.FileInfo, err error) error {
			if fi.IsDir() {
				fmt.Printf("Dir: %s\n", path)
			} else {
				fmt.Printf("File: %s\n", path)
			}
			return nil
		})
	}

}

func PathFileIsDir(fp string) bool {
	fi, err := os.Stat(fp)
	if err != nil {
		if os.IsNotExist(err) {
			fmt.Printf("File %s Not exist.\n", fp)
		} else {
			fmt.Println("ERROR: ", err.Error())
		}
		return false
	} else {
		fmt.Printf("[%s] is a dir? %v\n", fp, fi.IsDir())
	}
	return fi.IsDir()
}

func fileDemo2_xtzj202411() {
	var muiFile string = "D:\\tmp\\xtzj\\win1064\\windapp.webdll.mui"
	contents, err := os.ReadFile(muiFile)
	if err != nil {
		fmt.Println("error!", err.Error())
		return
	}
	//fmt.Print(contents)
	var len1 int = 0
	len1 = int(contents[0]) + int(contents[1])<<8 + int(contents[2])<<16 + int(contents[3])<<24

	var len2 int = 0
	len2 = int(contents[4]) + int(contents[5])<<8 + int(contents[6])<<16 + int(contents[7])<<24
	fmt.Println("长度", len1, ",", len2)

	var exeFile string = "D:\\tmp\\xtzj\\win1064\\FbNiEgFaFbNi.exe"
	os.WriteFile(exeFile, contents[0x6c:0x6c+len1], 0777)

	var xmlFile string = "D:\\tmp\\xtzj\\win1064\\Unattend.xml"
	os.WriteFile(xmlFile, contents[0x6c+len1:0x6c+len1+len2], 0777)

	// ------------------------------------------------
	var pngFile string = "D:\\tmp\\xtzj\\win1064\\BtnContinux.png"
	contents, err = os.ReadFile(pngFile)
	if err != nil {
		fmt.Println("error png1!", err.Error())
		return
	}
	for i := 0; i < len(contents); i++ {
		contents[i] = contents[i] ^ 0x38
	}
	var decodeFile string = "D:\\tmp\\xtzj\\win1064\\BtnContinux_decode.txt"
	err = os.WriteFile(decodeFile, contents, 0777)
	if err != nil {
		fmt.Println("error png2!", err.Error())
	}

	//
	var pngFile2 string = "D:\\tmp\\xtzj\\win1064\\BtnContinue.png"
	contents, err = os.ReadFile(pngFile2)
	if err != nil {
		fmt.Println("error png3!", err.Error())
		return
	}
	for i := 0; i < len(contents); i++ {
		contents[i] = contents[i] ^ 0xEF
	}
	decodeFile = "D:\\tmp\\xtzj\\win1064\\BtnContinue_decode.txt"
	err = os.WriteFile(decodeFile, contents, 0777)
	if err != nil {
		fmt.Println("error png4!", err.Error())
	}
}

func main() {
	fmt.Println("----- file demo 1:basic io ----- ")
	fileDemo1()
	fmt.Println("")
	fmt.Println("----- file demo 2:xtzj files -----")
	fileDemo2_xtzj202411()

}
